/*
 * @Description: 用户业务层
 * @Autor: HuiSir<273250950@qq.com>
 * @Date: 2020-11-09 09:59:34
 * @LastEditTime: 2020-11-28 12:39:53
 */

const { LOGIN_TIMEOUT, ERR, RES } = require('../../config') //配置
const UserModel = require('../db/models/user') //用户数据
const { creatToken, encrypt } = require('../utils/encryptToken') //加密
const redis = require('../utils/redis') //redis缓存

//注册，密码进行加密
const Signup = async (ctx) => {
    var postData = ctx.request.body //post数据

    ctx.response.type = 'json' //返回格式

    var findRes = await UserModel.findOne({ username: postData.username }) //比对

    if (!findRes) {
        //不存在
        postData.password = encrypt(postData.password) //密码加密
        const createRes = await UserModel.create(postData) //新增
        //返回结果
        ctx.response.body = RES.succ({
            msg: '注册成功',
            data: {
                _id: createRes._id,
                username: createRes.username,
                sex: createRes.sex,
                state: createRes.state,
            },
        })
    } else {
        ERR.e200('用户名已存在')
    }
}

//登录
const Login = async (ctx) => {
    //请求数据格式须为：x-www-form-urlencoded才能使用ctx.request.body接收
    let { username, password } = ctx.request.body

    //与数据库比对
    const res = await UserModel.findOne(
        { username, password: encrypt(password) },
        '-password -__v'
    ) //不返回密码

    ctx.response.type = 'json'

    if (res) {
        const token = creatToken(res._id)

        //这里将token存到redis(key为userid，value为token)
        //一个用户只有一条缓存，便于查询当前登陆用户数
        //注意配置失效时间
        const saveToken = await redis.set(
            res._id.toString(),
            token,
            'EX',
            LOGIN_TIMEOUT
        )

        //缓存成功则返回，否则报错
        if (saveToken == 'OK') {
            ctx.response.body = RES.succ({ data: res, token, msg: '登录成功' })
        }
    } else {
        ERR.e401('用户名或密码错误')
    }
}

//注销登陆
const Logout = async (ctx) => {
    //通过中间件的传值拿到token中的user信息，这里无需再次解析token
    const { userid } = ctx.curUserInfo

    //将用户从redis中删除
    const delToken = await redis.del(userid)

    ctx.response.type = 'json'

    //删除成功则返回，否则报错
    delToken == '1' && (ctx.response.body = RES.succ('注销登陆成功'))
}

//修改密码（重置密码后将登陆状态变为未登陆）
const ResetPass = async (ctx) => {
    //通过中间件的传值拿到token中的user信息，这里无需再次解析token
    const { userid } = ctx.curUserInfo

    const { oldPass, newPass } = ctx.request.body

    //与数据库比对
    const checkOldPass = await UserModel.findOne(
        { _id: userid, password: encrypt(oldPass) },
        '-password -__v'
    )

    if (!checkOldPass) {
        ERR.e200('旧密码不正确')
        return
    }

    if (oldPass === newPass) {
        ERR.e200('新密码不能与旧密码相同')
        return
    }

    //密码格式校验(可写可不写，因为可以前端校验)
    //...

    //修改密码
    const resetPass = await UserModel.updateOne(
        { _id: userid },
        { password: encrypt(newPass) }
    )

    if (resetPass.ok == 1) {
        //将用户从redis中删除,登陆状态变为未登陆
        const delToken = await redis.del(userid)

        if (delToken == '1') {
            ctx.response.type = 'json'
            ctx.response.body = RES.succ('修改密码成功')
        }
    }
}

//所有用户
const AllUser = async (ctx) => {
    //注意：这里必须使用await形式将异步转换为同步，要不然不能够返回数据
    let users = await UserModel.find({}, '-password -__v')
    ctx.response.type = 'json'
    ctx.response.body = RES.succ({ data: users })
    //处理错误在app.js中使用了中间件，这里不需要再次捕获
}

// 当前在线用户 GET (获取当前redis缓存中的用户)
const OnlineUsers = async (ctx) => {
    //查询缓存userid
    const getRedisUsers = await redis.keys('*')

    //读取user数据(多值查询，链式查询)
    let users = await UserModel.find({}, '-password -__v')
        .where('_id')
        .in(getRedisUsers)

    ctx.response.type = 'json'

    ctx.response.body = RES.succ({ data: users })
}

module.exports = {
    Signup,
    Login,
    Logout,
    AllUser,
    ResetPass,
    OnlineUsers,
}
